#pragma once

#include "fwddecl.h"
#include "viewport.h"
#include <cstdint>
#include <vector>
#include <GL\glew.h>
#include "bounded.h"
#include "impl\common_impl.h"
#include "impl\pipeline_impl.h"
#include "impl\cmdbuf_impl.h"
#include "types.h"
#include "integer_conversion.h"
#include "buffer.h"

namespace blu
{
	enum class primitive_topology
	{
		line_list,
		triangle_list,
	};

	class feeding_place
	{
		friend struct impl::implAccess;
	private:
		impl::FeedingPlace _impl;
		feeding_place(impl::FeedingPlace impl_)
			:_impl(impl_)
		{

		}
	};

	class feeding_convention
	{
		friend struct impl::implAccess;
	private:
		impl::InputConventionList _impl;
	public:
		feeding_place add(
			std::uint32_t stride_)
		{
			return impl::implAccess::makeThroughImpl<feeding_place>(_impl.alloc(stride_));
		}
	};

	class shader_input_var
	{
		friend struct impl::implAccess;
	private:
		impl::ExternalInputLocation _impl;
	};

	class fetching_convention
	{
		friend struct impl::implAccess;
	private:
		impl::FetchConventionList _impl;
	public:
		void add(
			const shader_input_var &extvar_,
			const feeding_place &fedplace_,
			std::uint32_t placeoffset_,
			type_identity type_)
		{
			_impl.add(
				impl::implAccess::getImpl(extvar_),
				impl::implAccess::getImpl(fedplace_),
				placeoffset_, type_, blu::integer_conversion::keep);
		}

		void add(
			const shader_input_var &extvar_,
			const feeding_place &fedplace_,
			std::uint32_t placeoffset_,
			type_identity type_,
			integer_conversion intconv_)
		{
			if (is_int_types(type_))
				throw std::invalid_argument("Only integer types may perform integer conversion.");
			_impl.add(
				impl::implAccess::getImpl(extvar_),
				impl::implAccess::getImpl(fedplace_),
				placeoffset_, type_, intconv_);
		}
	};

	class pipeline_args
	{
	private:
		primitive_topology _primtopo;
	public:
		pipeline_args(
			primitive_topology primtopo_,
			const feeding_convention &feedconv_,
			const fetching_convention &fetconv_)
		{

		}
	};

	class pipeline
	{
	private:
		pipeline_args _args;
	public:
		pipeline(
			renderpass &rpass_,
			const pipeline_args &args_)
			:_args(args_)
		{

		}
	};


	/* Feed vertices.
		Record a command into [cmdbuf_] to feed vertices resides in [vbuf_] to the feeding place [fedplace_],
		the vertices start from bytes-offset [vbufoffset_] and have formats assigned to that [fedplace_].
		When subsequent drawing commands fire the bounded pipeline,
		shader's input variables would fetch their values from corresponding feeding place.
	  <Prereqs>
		The [cmdbuf_] must have bounded with a pipeline, i.e. The [Preques] contains a [pipiline] condition. 
	*/
	template <typename... Prereqs>
	void feed(
		bounded<command_buffer, Prereqs...> &cmdbuf_,
		feeding_place fedplace_,
		const generic_buffer &vbuf_,
		std::uint32_t vbufoffset_)
	{
		static_assert(exist_condition<pipeline, Prereqs...>::value,
			"There must be a pipeline bounded to the command buffer before vertex buffers may fed.");

		impl::feedVertexBuffer(
			impl::implAccess::getImpl(cmdbuf_.host()),
			impl::implAccess::getImpl(fedplace_),
			impl::implAccess::getImpl(vbuf_),
			vbufoffset_);
		cmdbuf_.satisfy(vbuf_);
	}

	template <typename... Prereqs>
	void set_drawing_indices(
		bounded<command_buffer, Prereqs...> &cmdbuf_,
		const index_buffer &ibuf_,
		std::uint32_t ibufoffset_,
		type_identity indextype_)
	{
		static_assert(exist_condition<index_buffer, Prereqs...>::value,
			"There must be a pipeline bounded to the command buffer before index buffers may fed.");

		impl::feedIndexBuffer(
			impl::implAccess::getImpl(cmdbuf_.host()),
			impl::implAccess::getImpl(ibuf_),
			ibufoffset_,
			indextype_);
		cmdbuf_.satisfy(vbuf_);
	}
}